package org.refresh.core;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

import org.refresh.core.expr.BinaryExpression;
import org.refresh.core.expr.Expression;

import choco.Constraint;
import choco.ContradictionException;
import choco.Problem;
import choco.Solver;
import choco.Var;
import choco.integer.IntDomainVar;
import choco.integer.IntExp;
import choco.integer.IntVar;
import choco.integer.search.AssignVar;
import choco.integer.search.DecreasingDomain;
import choco.integer.search.IncreasingDomain;
import choco.integer.search.MinDomain;
import choco.integer.search.StaticVarOrder;

/*******************************************************************************
 * Copyright (c) 2005 Jules White. All rights reserved. This program and the
 * accompanying materials are made available under the terms of the Common
 * Public License v1.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors: Jules White - initial API and implementation
 ******************************************************************************/
public class RefreshSchedulingCore extends AbstractRefreshChocoCore {

	private class ResourceSpec {
		String resource;

		int max = 0;

		public int[] deltas;

		public int nonZeroDeltas;
	}

	private int minRequiredDeployed_ = 0;

	private IntDomainVar[] presenceVariables_;
	
	private Map<Object,IntDomainVar[]> localPresenceVariables_;

	private IntDomainVar[] starts_;

	private ResourceSpec getResourceSpec(String res) {
		return null;
	}

	private void initResourceProblem(String res) {
		int ctotal = sourceItems_.size();
		int ntotal = targetItems_.size();
		Problem pb = problem_;

		ResourceSpec spec = getResourceSpec(res);

		int capa = spec.max;
		int faketasks = spec.nonZeroDeltas;
		int n = ctotal + faketasks; // number of tasks (include the three fake
		// tasks)

		int[] heights_data = new int[n];
		for (int i = 0; i < ctotal; i++) {
			heights_data[i] = getValue(sourceItems_.get(i),
					sourcetVariableTable_, sourceVariableValuesTable_);
		}
		for (int i = ctotal; i < n; i++) {
			heights_data[i] = spec.deltas[i - ctotal];
		}

		int[] durations_data = new int[n];
		Arrays.fill(durations_data, 1);

		IntDomainVar[] starts = starts_;
		if(starts_ == null){
			starts_ = pb.makeBoundIntVarArray("start", n, 0,
				ntotal - 1);
			starts = starts_;
		}
		
		IntDomainVar[] ends = pb.makeBoundIntVarArray("end", n, 0, ntotal);

		IntDomainVar[] duration = new IntDomainVar[n];
		IntDomainVar[] height = new IntDomainVar[n];
		for (int i = 0; i < ctotal; i++) {
			duration[i] = (IntDomainVar) pb
					.makeConstantIntVar(durations_data[i]);
			height[i] = pb.makeEnumIntVar("duration " + i, new int[] { 0,
					heights_data[i] });
		}
		for (int i = ctotal; i < n; i++) {
			duration[i] = (IntDomainVar) pb.makeConstantIntVar(1);
			height[i] = pb.makeEnumIntVar("duration " + (i + ctotal),
					new int[] { 0, heights_data[i] });

			try {
				height[i].setVal(heights_data[i]);
				starts[i].setVal(n - ctotal);
				ends[i].setVal(n - (ctotal - 1));
			} catch (ContradictionException e) {
				throw new RuntimeException(e);
			}
		}

		IntDomainVar[] bool = pb.makeEnumIntVarArray("taskIn?", n, 0, 1);
		IntDomainVar obj = pb.makeBoundIntVar("obj", 0, n);
		// post the cumulative
		pb.post(pb.cumulative(starts, ends, duration, height, capa));

		// post the channeling to know if the task is scheduled or not
		for (int i = 0; i < n; i++) {
			pb.post(pb.boolChanneling(bool[i], height[i], heights_data[i]));
		}
		// state the objective function
		pb.post(pb.geq(pb.sum(bool), minRequiredDeployed_));
		// pb.solve();

		System.out.println("Objective : " + (obj.getVal() - 3));
		for (int i = 3; i < starts.length; i++) {
			if (height[i].getVal() != 0)
				System.out.println("[" + starts[i].getVal() + " - "
						+ (ends[i].getVal() - 1) + "]:" + height[i].getVal());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#getSolver()
	 */
	@Override
	protected Solver getSolver() {
		Solver s = problem_.getSolver();
		s.attachGoal(new AssignVar(new StaticVarOrder(presenceVariables_),
				new DecreasingDomain()));
		s.addGoal(new AssignVar(new MinDomain(problem_, starts_),
				new IncreasingDomain()));
		// s.setObjective(obj);
		s.setFirstSolution(true);
		// s.setDoMaximize(true);
		s.setRestart(false);

		return s;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addExcludesMappingConstraint(java.lang.Object,
	 *      java.lang.Object)
	 */
	@Override
	public void addExcludesMappingConstraint(Object src, Object req) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addRequiresMappingConstraint(java.lang.Object,
	 *      java.lang.Object)
	 */
	@Override
	public void addRequiresMappingConstraint(Object src, Object req) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addSelectMappingConstraint(java.lang.Object,
	 *      java.util.List, int, int)
	 */
	@Override
	public void addSelectMappingConstraint(Object src, List set, int min,
			int max) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addSelectMappingConstraint(java.lang.Object,
	 *      java.util.List, int)
	 */
	@Override
	public void addSelectMappingConstraint(Object src, List set, int exactcard) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addSourceMappedCardinalityConstraint(java.lang.Object,
	 *      int, int)
	 */
	@Override
	public void addSourceMappedCardinalityConstraint(Object src, int min,
			int max) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addSourceMappingConstraint(java.lang.Object,
	 *      org.refresh.core.expr.BinaryExpression)
	 */
	@Override
	public void addSourceMappingConstraint(Object src,
			BinaryExpression expression) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addTargetIntResourceConstraint(java.lang.Object,
	 *      java.lang.Object, int)
	 */
	@Override
	public void addTargetIntResourceConstraint(Object target, Object var,
			int val) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addTargetMappedSourcesCardinalityConstraint(java.lang.Object,
	 *      int, int)
	 */
	@Override
	public void addTargetMappedSourcesCardinalityConstraint(Object target,
			int min, int max) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#addTargetMappedSourcesConstraint(java.lang.Object,
	 *      int)
	 */
	@Override
	public void addTargetMappedSourcesConstraint(Object target, int exactmapped) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#getOrCreatePresenceVariable(java.lang.Object)
	 */
	@Override
	public IntVar getOrCreatePresenceVariable(Object item) {

		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#getTargets(java.lang.Object)
	 */
	@Override
	public List getTargets(Object src) {

		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#requireAllMappedExactlyOnce()
	 */
	@Override
	public void requireAllMappedExactlyOnce() {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#setInValidTargets(java.lang.Object,
	 *      java.util.List)
	 */
	@Override
	public void setInValidTargets(Object src, List invalid) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.AbstractRefreshChocoCore#setValidTargets(java.lang.Object,
	 *      java.util.List)
	 */
	@Override
	public void setValidTargets(Object src, List valid) {

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.refresh.core.ChocoRefreshCore#getTargetTable(java.lang.Object)
	 */
	public Map<Object, IntVar> getTargetTable(Object src) {

		return null;
	}

}
